package com.king.juc.reentrantlock;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ReentrantLock 案例演示
 *
 * @author king
 * @date 2020/11/20 17:17
 */
@Slf4j(topic = "c.ReentrantLockDemo")
public class ReentrantLockDemo {
    static ReentrantLock lock = new ReentrantLock();

    /**
     * 休息室
     */
    private static final Object room = new Object();
    /**
     * 烟
     */
    private static boolean hasCigarette = false;
    /**
     * 外卖
     */
    private static boolean hasTakeout = false;
    private static ReentrantLock ROOM = new ReentrantLock();
    /**
     * 等待烟的休息室
     */
    private static Condition waitCigaretteSet = ROOM.newCondition();
    /**
     * 等待外卖的休息室
     */
    private static Condition waitTakeoutSet = ROOM.newCondition();

    public static void main(String[] args) {
        // 可重入演示
//        reentrant1();
        // 可打断演示
//        canBeInterrupted();
        // 锁超时
//        lockTimeout();
        // 条件变量
        condition();
    }

    /**
     * 可重入演示
     */
    private static void reentrant1() {
        lock.lock();
        try {
            log.debug("execute reentrant1");
            reentrant2();
        } finally {
            lock.unlock();
        }
    }

    private static void reentrant2() {
        lock.lock();
        try {
            log.debug("execute reentrant2");
            reentrant3();
        } finally {
            lock.unlock();
        }
    }

    private static void reentrant3() {
        lock.lock();
        try {
            log.debug("execute reentrant2");
        } finally {
            lock.unlock();
        }
    }

    /**
     * 可打断演示
     */
    private static void canBeInterrupted() {

        Thread t1 = new Thread(() -> {
            log.debug("尝试获取锁...");
            try {
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                log.error("没有获得锁", e);
                return;
            }

            try {
                log.debug("获取到锁...");
            } finally {
                lock.unlock();
            }
        }, "t1");

        lock.lock();
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("打断...");
        t1.interrupt();
    }

    /**
     * 锁超时
     */
    private static void lockTimeout() {
        Thread t1 = new Thread(() -> {
            log.debug("启动...");
//            if (!lock.tryLock()) {
            try {
                if (!lock.tryLock(1, TimeUnit.SECONDS)) {
                    log.debug("获取立即失败, 返回");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                log.debug("获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");

        lock.lock();
        log.debug("获得了锁");
        t1.start();
        try {
            Thread.sleep(2000);
//            Thread.sleep(0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 条件变量
     */
    private static void condition() {
        new Thread(() -> {
            ROOM.lock();
            try {
                log.debug("有烟没? [{}]", hasCigarette);
                while (!hasCigarette) {
                    log.debug("没烟, 先歇会!");
                    try {
                        waitCigaretteSet.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("可以开始干活了");
            } finally {
                ROOM.unlock();
            }
        }, "小南").start();

        new Thread(() -> {
            ROOM.lock();
            try {
                log.debug("外卖送到没? [{}]", hasTakeout);
                while (!hasTakeout) {
                    log.debug("没外卖, 先歇会!");
                    try {
                        waitTakeoutSet.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("可以开始干活了");
            } finally {
                ROOM.unlock();
            }
        }, "小女").start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            ROOM.lock();
            try {
                hasTakeout = true;
                log.debug("外卖到了");
                waitTakeoutSet.signal();
            }finally {
                ROOM.unlock();
            }
        }, "送外卖的").start();

        new Thread(() -> {
            ROOM.lock();
            try {
                hasCigarette = true;
                log.debug("烟到了");
                waitCigaretteSet.signal();
            }finally {
                ROOM.unlock();
            }
        }, "送烟的").start();
    }
}
